package indi.gao.leetcode.thread;

/**
 * @Description: 三个不同的线程将会共用一个 Foo 实例。
 * <p>
 * 线程 A 将会调用 one() 方法
 * 线程 B 将会调用 two() 方法
 * 线程 C 将会调用 three() 方法
 * 请设计修改程序，以确保 two() 方法在 one() 方法之后被执行，three() 方法在 two() 方法之后被执行。
 * <p>
 * 输入: [1,2,3]
 * 输出: "onetwothree"
 * 解释:
 * 有三个线程会被异步启动。
 * 输入 [1,2,3] 表示线程 A 将会调用 one() 方法，线程 B 将会调用 two() 方法，线程 C 将会调用 three() 方法。
 * 正确的输出是 "onetwothree"。
 * 示例 2:
 * <p>
 * 输入: [1,3,2]
 * 输出: "onetwothree"
 * 解释:
 * 输入 [1,3,2] 表示线程 A 将会调用 one() 方法，线程 B 将会调用 three() 方法，线程 C 将会调用 two() 方法。
 * 正确的输出是 "onetwothree"。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/print-in-order
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 * @create: 2020/2/29 0:20
 * @update logs
 * @throws Exception
 */
public class PrintInOrder {

    public static void main(String[] args) throws InterruptedException {
        Foo foo = new Foo();
        int order[] = {1, 3, 2};
        Runnable one = new Runnable() {
            @Override
            public void run() {
                System.out.println("one");
            }
        };
        Runnable two = new Runnable() {
            @Override
            public void run() {
                System.out.println("two");
            }
        };
        Runnable three = new Runnable() {
            @Override
            public void run() {
                System.out.println("three");
            }
        };
        Thread threadOne = new Thread(one);
        Thread threadTwo = new Thread(two);
        Thread threadThree = new Thread(three);
        for (int i : order) {
            if (i == 1) {
                threadOne.start();
            } else if (i == 3) {
                threadTwo.start();
            } else {
                threadThree.start();
            }
        }
    }
}

class Foo {

    private boolean isFirst = false;
    private boolean isSecond = false;
    private final Object lock = new Object();

    public Foo() {

    }

    public void first(Runnable printFirst) throws InterruptedException {
        synchronized (lock) {
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            isFirst = true;
            lock.notifyAll();
        }
    }

    public void second(Runnable printSecond) throws InterruptedException {

        synchronized (lock) {
            while (!isFirst) {
                lock.wait();
            }
// printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            isSecond = true;
            lock.notifyAll();
        }
    }

    public void third(Runnable printThird) throws InterruptedException {

        synchronized (lock) {
            while (!isSecond) {
                lock.wait();
            }
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
            lock.notifyAll();
        }
    }
}